package com.zkhz.base.security.config;

import com.google.common.collect.Lists;
import com.zkhz.base.common.service.CurrentUserInfoService;
import com.zkhz.base.mapper.MyUserMapper;
import com.zkhz.base.security.authentication.handler.AjaxLogoutSuccessHandler;
import com.zkhz.base.security.authentication.handler.JwtAuthenticationFailureHandler;
import com.zkhz.base.security.authentication.handler.JwtAuthenticationSuccessHandler;
import com.zkhz.base.security.authentication.userdetails.TenantUserDetailsServiceImpl;
import com.zkhz.base.security.authentication.userdetails.model.SecurityUserDetails;
import com.zkhz.base.security.token.JwtUserAuthenticationConverter;
import com.zkhz.base.security.token.UserInfoJwtAccessTokenConverter;
import com.zkhz.base.security.util.MyPasswordEncoder;
import com.zkhz.base.util.system.SpringBeansUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @Description security用到的全局配置
 *
 * @author wuzeqin
 * @date 2020/5/26
 **/
@Configuration
@EnableConfigurationProperties(com.zkhz.base.security.config.SystemSecurityProperties.class)
public class SecurityGlobalConfiguration {

//    @Bean(name = "BCryptPasswordEncoder")
//    public BCryptPasswordEncoder BCryptPasswordEncoder() {
//        return new BCryptPasswordEncoder();
//    }

    @Bean
    @ConditionalOnMissingBean
    public UserDetailsService userDetailsService(MyUserMapper myUserMapper, BCryptPasswordEncoder bCryptPasswordEncoder) {
        return new TenantUserDetailsServiceImpl(myUserMapper, bCryptPasswordEncoder);
    }

    @Bean
    @ConditionalOnMissingBean
    public PasswordEncoder passwordEncoder() {
        BCryptPasswordEncoder bCryptPasswordEncoder = SpringBeansUtil.getBean("BCryptPasswordEncoder", BCryptPasswordEncoder.class);
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                return rawPassword == null ? null : rawPassword.toString();
            }
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                if (rawPassword == null && encodedPassword == null) {
                    return true;
                }
                return bCryptPasswordEncoder.matches(rawPassword, encodedPassword);
            }
//            @Override
//            public boolean matches(CharSequence rawPassword, String encodedPassword) {
//                if (rawPassword == null && encodedPassword == null) {
//                    return true;
//                }
//                if (rawPassword == null) {
//                    return false;
//                }
//                String loginPrivateKey = SpringBeansUtil.getBean(BankServiceManager.class).getLoginPrivateKey();
//                String pwd = "";
//                try {
//                    pwd = RSAUtil.decrypt(new String(Base64.getDecoder().decode(rawPassword.toString())), loginPrivateKey);
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//                return pwd.equals(encodedPassword);
//            }
        };
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth,
                                UserDetailsService userDetailsService,
                                MyPasswordEncoder passwordEncoder,
                                @Autowired(required = false) List<AuthenticationProvider> providers) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
        if (providers != null) {
            providers.forEach(auth::authenticationProvider);
        }
    }

    @Bean
    @ConditionalOnMissingBean
    public CorsConfiguration corsConfiguration() {
        CorsConfiguration conf = new CorsConfiguration();
        conf.setAllowedMethods(Lists.newArrayList("GET", "PUT", "PATCH", "POST", "DELETE", "OPTIONS"));
        conf.setAllowCredentials(Boolean.TRUE);
        conf.applyPermitDefaultValues();
        return conf;
    }

    @Bean
    @ConditionalOnMissingBean
    public CorsConfigurationSource corsConfigurationSource(CorsConfiguration corsConfiguration) {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }

    /**
     * @return 登录成功处理器
     */
    @Bean
    @ConditionalOnMissingBean
    public AuthenticationSuccessHandler authenticationSuccessHandler(AuthorizationServerTokenServices tokenServices) {
        //AuthenticationSuccessHandler handler = new JwtAuthenticationSuccessHandler();
        String clientId = SystemSecurityProperties.getInstance().getClientId();
        return new JwtAuthenticationSuccessHandler(tokenServices, clientId);
    }

    /**
     * @return 登录失败处理器
     */
    @Bean
    @ConditionalOnMissingBean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return new JwtAuthenticationFailureHandler();
    }

    /**
     * @return 注销成功处理器
     */
    @Bean
    @ConditionalOnMissingBean
    public LogoutSuccessHandler logoutSuccessHandler() {
        return new AjaxLogoutSuccessHandler();
    }

    /**
     * 根据token提取认证用户
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultTokenServices tokenServices(TokenStore tokenStore, JwtAccessTokenConverter converter) {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore);
        tokenServices.setTokenEnhancer(converter);
        SystemSecurityProperties securityProperties = SystemSecurityProperties.getInstance();
        if(securityProperties.getJwt().getAccessTokenValidityHours() > 0) {
            tokenServices.setAccessTokenValiditySeconds(securityProperties.getJwt().getAccessTokenValidityHours() * 60 * 60);
        }
        if(securityProperties.getJwt().getRefreshTokenValidityDays() > 0) {
            tokenServices.setRefreshTokenValiditySeconds(securityProperties.getJwt().getRefreshTokenValidityDays() * 60 * 60 * 24);
        }

        return tokenServices;
    }

    /**
     * token仓库，这里有jwt
     * @param converter
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public TokenStore tokenStore(JwtAccessTokenConverter converter) {
        return new JwtTokenStore(converter);
    }

    /**
     * jwt转换处理器
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public JwtAccessTokenConverter jwtAccessTokenConverter(UserAuthenticationConverter userAuthenticationConverter) {
        JwtAccessTokenConverter converter = new UserInfoJwtAccessTokenConverter();
        converter.setSigningKey(SystemSecurityProperties.getInstance().getJwt().getSigningKey());
        ((DefaultAccessTokenConverter) converter.getAccessTokenConverter()).setUserTokenConverter(userAuthenticationConverter);
        return converter;
    }

    @Bean
    @ConditionalOnMissingBean
    public UserAuthenticationConverter userAuthenticationConverter(UserDetailsService userDetailsService) {
        return new JwtUserAuthenticationConverter(userDetailsService);
    }

    //当前用户信息服务
    @Bean
    @ConditionalOnMissingBean
    public CurrentUserInfoService currentUserInfoService() {
        return new CurrentUserInfoService() {
            @Override
            public <T> T getProperty(String key, Class<T> clazz) {
                return clazz.cast(getUserInfo().get(key));
            }

            @Override
            public Map<String, ?> getUserInfo() {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication != null && authentication.getPrincipal() instanceof SecurityUserDetails) {
                    return ((SecurityUserDetails) authentication.getPrincipal()).getUserInfo();
                }
                return Collections.emptyMap();
            }
        };
    }


    @Bean  //如果没有配置国际化，spring security 模块就用SpringSecurityMessageSource
    @ConditionalOnExpression("'${spring.messages.basename:}'.isEmpty()")
    public MessageSource messageSource() {
        return new SpringSecurityMessageSource();
    }

}
